package com.cabal.common.config;

import com.cabal.common.dto.ResultDTO;
import com.cabal.common.errcode.ErrorCodeEnum;
import com.cabal.common.util.TmsServiceException;
import com.cabal.module.security.DirectReturnResult;
import com.cabal.module.security.SecurityAdminNoCheck;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.net.URI;
import java.util.List;
import java.util.Set;

/**
 * 异常捕获统一返回处理
 *
 * @author shenkang
 * @data 2022年5月6日
 */
@Slf4j
@RestControllerAdvice
public class MyResponseBody implements ResponseBodyAdvice {


    @ExceptionHandler(value = {Exception.class})
    public ResultDTO globalExceptionHandler(Exception e) {
        log.error("系统错误：", e);
        ResultDTO resultDTO = new ResultDTO();
        resultDTO.setCode(ErrorCodeEnum.SYSTOM_ERROR.getCode());
        resultDTO.setMessage("系统错误");
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
            resultDTO.setCode(ErrorCodeEnum.PARAM_ERROR.getCode());

            Set<ConstraintViolation<?>> fieldErrors = constraintViolationException.getConstraintViolations();
            StringBuffer messageBuffer = new StringBuffer();
            fieldErrors.forEach(fieldError -> {
                String field = fieldError.getPropertyPath().toString();
                String defaultMessage = fieldError.getMessage();
                messageBuffer.append("字段[").append(field).append("]=").append(defaultMessage).append(";");
            });
            if (StringUtils.isNotEmpty(messageBuffer.toString())) {
                resultDTO.setMessage(messageBuffer.deleteCharAt(messageBuffer.length() - 1).toString());
            }
        } else if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
            resultDTO.setCode(ErrorCodeEnum.PARAM_ERROR.getCode());
            BindingResult bindingResult = ex.getBindingResult();
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            StringBuffer messageBuffer = new StringBuffer();
            fieldErrors.forEach(fieldError -> {
                String field = fieldError.getField();
                String defaultMessage = fieldError.getDefaultMessage();
                messageBuffer.append("字段[").append(field).append("]=").append(defaultMessage).append(";");
            });
            if (StringUtils.isNotEmpty(messageBuffer.toString())) {
                resultDTO.setMessage(messageBuffer.deleteCharAt(messageBuffer.length() - 1).toString());
            }
        }
        resultDTO.setTime(System.currentTimeMillis());
        resultDTO.setSuccess(false);
        return resultDTO;
    }


    @ExceptionHandler(value = {TmsServiceException.class})
    public ResultDTO BusinessExceptionHandler(TmsServiceException e) {
        log.error("返回异常结果：", e);
        ResultDTO resultDTO = new ResultDTO();
        resultDTO.setCode(e.getCode());
        resultDTO.setMessage(e.getMessage());
        resultDTO.setTime(System.currentTimeMillis());
        resultDTO.setSuccess(false);
        return resultDTO;
    }


    @Override
    public Object beforeBodyWrite(Object resBody, MethodParameter arg1, MediaType arg2, Class arg3, ServerHttpRequest req,
                                  ServerHttpResponse res) {
        if(arg1.getMethod().getAnnotation(DirectReturnResult.class) != null) {
            return resBody;
        }

        URI uri = req.getURI();
        if (uri.toString().contains("swagge")
                || uri.toString().contains("error")
                || uri.toString().contains("api-docs")
                || uri.toString().contains("api/cabal/cabalUpdateClient")
                ) {
            return resBody;
        }
        ResultDTO returnVO = new ResultDTO();
        returnVO.setTime(System.currentTimeMillis());
        log.info("请求接口uri：{}", uri);
        if (resBody != null && resBody.toString().length() < 2000) {
            // 有接口返回是base64
            log.info("返回结果：{}", resBody);
        }
        try {
            // 如果方法的执行结果是ReturnVO，则将该对象直接返回
            if (resBody instanceof ResultDTO) {
                returnVO = (ResultDTO) resBody;
            } else {
                // 否则，就要封装到ReturnVO的data中
                returnVO.setData(resBody);
            }
        } catch (Exception e) {
            log.error("系统异常", e);
            // 如果出现了异常，调用异常处理方法将错误信息封装到ReturnVO中并返回
        }
        return returnVO;
    }

    @Override
    public boolean supports(MethodParameter arg0, Class arg1) {
        // 这里直接返回true,表示对任何handler的responsebody都调用beforeBodyWrite方法
        if(arg0.getMethod().getName().equals("loadAppSecret")) {
            return false;
        }
        return true;
    }
}
